Skip to main content

Compiling to WebAssembly

The compiler can build a program as WebAssembly instead of a native executable. The result runs in Node.js or in a web browser, and prints exactly what the native build prints.

flowwing hello.fg --target=wasm32 --emit=exe -o hello.js
│
▼
hello.js the loader: starts the program and connects its output
hello.wasm your program and the Flow-Wing runtime

What you need​

  • Emscripten (emsdk), which does the final link to WebAssembly. Install it from emscripten.org. The compiler finds it without extra setup if emcc is on your PATH, if EMSDK points at the emsdk folder, or if emsdk is installed in ~/emsdk. To choose one explicitly, set FLOWWING_EMCC to the emcc you want.
  • Node.js 18 or newer, to run the result outside a browser.

Build and run with Node​

fun square(n: int) -> int {
  return n * n
}

println("Hello from WebAssembly")
println(square(7))
flowwing hello.fg --target=wasm32 --emit=exe -o out/hello.js
node out/hello.js
Hello from WebAssembly
49

-o decides the output: a name ending in .js (or with no extension) gives a program for Node, and a name ending in .html gives a web page.

Build a web page​

flowwing hello.fg --target=wasm32 --emit=exe -o web/hello.html

This writes hello.html, hello.js and hello.wasm. Browsers do not load WebAssembly from a file:// address, so serve the folder with any web server and open the page:

cd web && python3 -m http.server 8000
# then open http://localhost:8000/hello.html

The page shows the program's output. Your program runs on the page's own thread, so a program that runs for a long time, or calls sys::sleep, keeps the page busy until it finishes. To keep the page responsive, run the .js file in a Web Worker instead, as the playground does.

Build an interactive page​

To work with the page itself (read what is typed, change elements, handle clicks, fetch data), bring the dom module, build to app.js, and load it from your own HTML:

flowwing app.fg --target=wasm32 -o app.js
<button id="add">+</button> <span id="count">0</span>
<script src="app.js"></script>

While it waits for the next event, the program sleeps and the page stays responsive. For browser APIs dom does not cover, the js module reaches any JavaScript value.

What works​

The whole language, including classes, inheritance, the garbage collector, spawn with sys::yield and sys::sleep, and these modules: vec, map, text, io, json, Err, sys and file, plus dom and js, which work only here.

In Node, a program sees the real disk and the real standard input, and sys::run and sys::exec run their command, all as a native build would.

A browser has none of these. There, file works on files kept in memory for the length of the run, standard input is whatever text the page supplies, and sys::run returns -1 with an error message, because a page has no shell.

What does not work yet​

Why
vortex, mongo, raylibThey need what WebAssembly cannot reach: a listening socket, a database connection, a window. Bringing one stops the build with ModuleNotForTarget.
Very deep recursionEvery WebAssembly call also uses the JavaScript engine's own stack, which stops recursion at roughly 10,000 to 20,000 calls, and a task also has its own stack. The program then ends with Runtime Error: Stack Overflow. A native main reaches far deeper.

Seeing the generated code​

--emit=ir with --target=wasm32 writes the LLVM IR for the WebAssembly target, where pointers are 4 bytes instead of 8:

flowwing hello.fg --target=wasm32 --emit=ir --output-dir=ir

Size and speed​

A small program is about 30 to 60 KB of WebAssembly, most of it the runtime. Programs that use spawn are larger and somewhat slower, because switching between tasks needs extra machinery in WebAssembly; programs that do not spawn do not pay for it.

If something goes wrong​

MessageWhat to do
needs Emscripten (emcc), which was not foundInstall emsdk, or point EMSDK or FLOWWING_EMCC at it.
The Flow-Wing runtime for wasm32 is not builtYour install is missing the WebAssembly runtime. Reinstall a release that includes it.
--target=wasm32 cannot --emit=obj or --emit=jitObject files and the JIT are native only.
[Error:ModuleNotForTarget] The 'vortex' module works only in native builds.The module needs a native build (see above).
[Error:ModuleNotForTarget] The 'dom' module works only in WebAssembly buildsBuild with --target=wasm32: dom and js talk to a web page.